﻿// HSS.Forms.Toolbox.cs
// ----------------------------------------------------------------------------
// Licensed under the MIT license
// http://www.opensource.org/licenses/mit-license.html
// ----------------------------------------------------------------------------
// HighSpeed-Solutions, LLC
// Copyright (c) 2001-2010
// ----------------------------------------------------------------------------
// File:       Toolbox.cs
// Author:     HSS\gbanta
// Created:    08/12/2010
// Modified:   12/04/2010
// ----------------------------------------------------------------------------
namespace HSS.Forms
{
	#region Using Directives
	using System;
	using System.ComponentModel;
	using System.Drawing;
	using System.Drawing.Drawing2D;
	using System.Windows.Forms;
	#endregion

	#region Toolbox
	/// <summary>
	/// A UserControl listing components or controls that can be dragged to a design surface
	/// </summary>
	[Description("A UserControl listing components or controls that can be dragged to a design surface")]
	public partial class Toolbox : ExplorerView
	{
		#region Consts
		/// <summary>
		/// Used to turn off the default tooltips
		/// </summary>
		private const int TVS_NOTOOLTIPS = 0x80;
		/// <summary>
		/// Used to turn off Horizontal Scrolling
		/// </summary>
		private const int TVS_NOHSCROLL = 0x8000;
		#endregion

		#region Fields
		/// <summary>
		/// Custom tooltip for the treenodes.
		/// </summary>
		private ToolTip _toolTip;
		/// <summary>
		/// Stores the last mouse position for the custom tooltip.
		/// </summary>
		private TreeNode _previousNode;
		/// <summary>
		/// Textbox used for label edit.
		/// </summary>
		private TextBox _labelEditBox;
		#endregion

		#region Constructor
		/// <summary>
		/// Creates a new Toolbox.
		/// </summary>
		public Toolbox()
		{
			InitializeComponent();

			this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
			this.SetStyle(ControlStyles.EnableNotifyMessage, true);

			this.DrawMode = TreeViewDrawMode.OwnerDrawAll;
			this.ItemHeight = 20;
			this.FullRowSelect = true;
			this.ShowLines = false;
			this.HotTracking = true;
			this.HideSelection = false;
			this.ShowPlusMinus = false;
			this.ShowRootLines = false;
			this.ShowNodeToolTips = true;
			this.CheckBoxes = false;

			this._previousNode = null;
			this._toolTip = new ToolTip();

			this._labelEditBox = new TextBox();
			this._labelEditBox.BorderStyle = BorderStyle.None;
			this._labelEditBox.Visible = false;
			this._labelEditBox.LostFocus += new EventHandler(mLabelEditBox_LostFocus);
			this._labelEditBox.KeyDown += new KeyEventHandler(mLabelEditBox_KeyDown);

			this.Controls.Add(this._labelEditBox);
		}
		#endregion

		#region Overrides
		/// <summary>
		/// Gets the create params property.
		/// Disables the tooltip activity for the treenodes.
		/// </summary>
		protected override CreateParams CreateParams
		{
			get
			{
				CreateParams p = base.CreateParams;
				p.Style = p.Style | TVS_NOTOOLTIPS;
				p.Style = p.Style | TVS_NOHSCROLL;
				return p;
			}
		}
		/// <summary>
		/// Decides wich node type should be drawn and calls the coresponding.
		/// methods.
		/// </summary>
		protected override void OnDrawNode(DrawTreeNodeEventArgs e)
		{
			if (e.Node.Level == 0)
				DrawGroupItem(e);
			else
				DrawItem(e);
		}
		/// <summary>
		/// Handles the mouse down event
		/// 
		/// Selects the node under the mouse cursor.
		/// </summary>
		/// <param name="e"></param>
		protected override void OnMouseDown(MouseEventArgs e)
		{
			TreeViewHitTestInfo hitTestInfo = this.HitTest(e.X, e.Y);

			if (hitTestInfo.Node != null &&
				hitTestInfo.Location != TreeViewHitTestLocations.PlusMinus)
			{
				ToolboxNode node = hitTestInfo.Node as ToolboxNode;
				if (null == node)
				{
					base.OnMouseDown(e);
					return;
				}
				else if (node.Enabled)
				{
					this.SelectedNode = hitTestInfo.Node;
					if (hitTestInfo.Node.Level == 0 && e.Button == MouseButtons.Left)
					{
						if (hitTestInfo.Node.IsExpanded)
							hitTestInfo.Node.Collapse();
						else
							hitTestInfo.Node.Expand();
					}
				}
			}

			base.OnMouseDown(e);
		}
		/// <summary>
		/// Handles the mouse move event
		/// 
		/// Checks if the node under the cursor contains a tooltip
		/// and display it.
		/// </summary>
		/// <param name="e"></param>
		protected override void OnMouseMove(MouseEventArgs e)
		{
			base.OnMouseMove(e);

			ToolboxNode currentNode = GetNodeAt(e.X, e.Y) as ToolboxNode;

			if (currentNode == null || this._previousNode == currentNode)
				return;

			if (currentNode.ToolTipText == String.Empty)
			{
				// This item doesn'n contain a tooltip text.
				// Hide the tooltip.
				if (this._toolTip != null)
				{
					this._toolTip.Dispose();
					this._toolTip = null;
				}

				return;
			}

			// Store the current node.
			this._previousNode = currentNode;

			string toolTipCaption = currentNode.ToolTipCaption;
			string toolTipText = currentNode.ToolTipText;

			// Turn off the tooltip so we can change the text.
			if (this._toolTip != null && this._toolTip.Active)
			{
				this._toolTip.Dispose();
				this._toolTip = null;
			}

			// Change the tooltip text.
			this._toolTip = new ToolTip();
			this._toolTip.ToolTipTitle = toolTipCaption;
			this._toolTip.SetToolTip(this, toolTipText);

			// Turn on the tooltip .
			this._toolTip.Active = true;

		}
		/// <summary>
		/// Handles the OnBeforeLabelEdit event
		/// 
		/// Adds a textbox to the current label position to
		/// handle the label editing by itself.
		/// </summary>
		/// <param name="e"></param>
		protected override void OnBeforeLabelEdit(NodeLabelEditEventArgs e)
		{
			e.CancelEdit = true;

			ToolboxNode node = e.Node as ToolboxNode;
			if (null == node)
				return;

			if (node.Enabled)
			{
				node.IsInEdit = true;

				if (node.Level == 0)
				{
					_labelEditBox.Font = new Font(this.GroupHeaderFont, FontStyle.Bold);
					_labelEditBox.Update();
					_labelEditBox.Bounds = new Rectangle(15, e.Node.Bounds.Top + 3, this.Width - 22, e.Node.Bounds.Height + 4);
					_labelEditBox.Update();
				}
				else
				{
					_labelEditBox.Font = new Font(this.Font, FontStyle.Regular);
					_labelEditBox.Bounds = new Rectangle(22, e.Node.Bounds.Top + 3, this.Width - 27, e.Node.Bounds.Height + 4);
				}
				_labelEditBox.Text = e.Node.Text;
				_labelEditBox.Visible = true;
				_labelEditBox.Tag = e.Node;
				_labelEditBox.Focus();
				_labelEditBox.SelectAll();
			}
		}
		/// <summary>
		/// Handles the OnBeforeSelect event
		/// </summary>
		/// <param name="e"></param>
		protected override void OnBeforeSelect(TreeViewCancelEventArgs e)
		{
			ToolboxNode node = e.Node as ToolboxNode;
			if (null == node)
				e.Cancel = true;
			else if (!node.Enabled)
				e.Cancel = true;
			base.OnBeforeSelect(e);
		}
		#endregion

		#region New
		/// <summary>
		/// Gets or sets the mode in which the control is drawn
		/// </summary>
		[Browsable(false)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		new public TreeViewDrawMode DrawMode
		{
			get { return base.DrawMode; }
			set { base.DrawMode = value; }
		}
		/// <summary>
		/// Gets or sets the height of each tree node in the tree view control
		/// </summary>
		[Browsable(false)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		new public int ItemHeight
		{
			get { return base.ItemHeight; }
			set { base.ItemHeight = value; }
		}
		/// <summary>
		/// Gets or sets a value indicating whether the selection highlight
		/// spans the width of the tree view control
		/// </summary>
		[Browsable(false)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		new public bool FullRowSelect
		{
			get { return base.FullRowSelect; }
			set { base.FullRowSelect = value; }
		}
		/// <summary>
		/// Gets or sets a value indicating whether lines are drawn
		/// between tree nodes in the tree view control
		/// </summary>
		[Browsable(false)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		new public bool ShowLines
		{
			get { return base.ShowLines; }
			set { base.ShowLines = value; }
		}
		/// <summary>
		/// Gets or sets a value indicating whether a tree node label takes on 
		/// the appearance of a hyperlink as the mouse pointer passes over it
		/// </summary>
		[Browsable(false)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		new public bool HotTracking
		{
			get { return base.HotTracking; }
			set { base.HotTracking = value; }
		}
		/// <summary>
		/// Gets or sets a value indicating whether the selected tree node remains
		/// highlighted even when the tree view has lost the focus
		/// </summary>
		[Browsable(false)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		new public bool HideSelection
		{
			get { return base.HideSelection; }
			set { base.HideSelection = value; }
		}
		/// <summary>
		/// Gets or sets a value indicating whether plus-sign (+) and minus-sign (-)
		/// buttons are displayed next to tree nodes that contain child tree nodes
		/// </summary>
		[Browsable(false)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		new public bool ShowPlusMinus
		{
			get { return base.ShowPlusMinus; }
			set { base.ShowPlusMinus = value; }
		}
		/// <summary>
		/// Gets or sets a value indicating whether lines are drawn between the 
		/// tree nodes that are at the root of the tree view
		/// </summary>
		[Browsable(false)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		new public bool ShowRootLines
		{
			get { return base.ShowRootLines; }
			set { base.ShowRootLines = value; }
		}
		/// <summary>
		/// Gets or sets a value indicating ToolTips are shown when the mouse 
		/// pointer hovers over a System.Windows.Forms.TreeNode
		/// </summary>
		[Browsable(false)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		new public bool ShowNodeToolTips
		{
			get { return base.ShowNodeToolTips; }
			set { base.ShowNodeToolTips = value; }
		}
		/// <summary>
		/// Gets or sets a value indicating whether check boxes are displayed next
		/// to the tree nodes in the tree view control
		/// </summary>
		[Browsable(false)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		new public bool CheckBoxes
		{
			get { return base.CheckBoxes; }
			set { base.CheckBoxes = value; }
		}
		/// <summary>
		/// Gets or sets a value indicating whether the tree view control displays
		/// scroll bars when they are needed
		/// </summary>
		[Browsable(false)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		new public bool Scrollable
		{
			get { return base.Scrollable; }
			set { base.Scrollable = value; }
		}
		/// <summary>
		/// Gets or sets a value indicating whether the label text of the tree
		/// nodes can be edited
		/// </summary>
		[Browsable(false)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		new public bool LabelEdit
		{
			get { return base.LabelEdit; }
			set { base.LabelEdit = value; }
		}
		/// <summary>
		/// Gets or sets the distance to indent each of the child tree node levels
		/// </summary>
		[Browsable(false)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		new public int Indent
		{
			get { return base.Indent; }
			set { base.Indent = value; }
		}
		#endregion

		#region Properties
		/// <summary>
		/// Gets or sets the Font to be used for the group headers in the toolbox
		/// </summary>
		[Category("Appearance")]
		[Description("Gets or sets the Font to be used for the group headers in the toolbox")]
		[DefaultValue(typeof(Font), "Segoe UI, 9pt, style=Bold")]
		public Font GroupHeaderFont
		{
			get { return _groupHeaderFont; }
			set { _groupHeaderFont = value; }
		} Font _groupHeaderFont = new Font("Segoe UI", 9, FontStyle.Bold, GraphicsUnit.Point);
		/// <summary>
		/// The backcolor of a selected item
		/// </summary>
		[Category("Appearance")]
		[Description("The backcolor of a selected item")]
		[DefaultValue(typeof(Color), "206,237,250")]
		public Color SelectedBackColor
		{
			get { return _selectedBackColor; }
			set { _selectedBackColor = value; }
		} Color _selectedBackColor = Color.FromArgb(206, 237, 250);
		/// <summary>
		/// The backcolor of a hovered item
		/// </summary>
		[Category("Appearance")]
		[Description("The backcolor of a hovered item")]
		[DefaultValue(typeof(Color), "206,237,250")]
		public Color HoverBackColor
		{
			get { return _hoverBackColor; }
			set { _hoverBackColor = value; }
		} Color _hoverBackColor = Color.FromArgb(206, 237, 250);
		/// <summary>
		/// The first backcolor of a Group Header
		/// </summary>
		[Category("Appearance")]
		[Description("The first backcolor of a Group Header")]
		[DefaultValue(typeof(Color), "250, 250, 255")]
		public Color GroupBackColor1
		{
			get { return _groupBackColor1; }
			set { _groupBackColor1 = value; }
		} Color _groupBackColor1 = Color.FromArgb(250, 250, 255);
		/// <summary>
		/// The second backcolor of a Group Header
		/// </summary>
		[Category("Appearance")]
		[Description("The second backcolor of a Group Header")]
		[DefaultValue(typeof(Color), "200, 205, 218")]
		public Color GroupBackColor2
		{
			get { return _groupBackColor2; }
			set { _groupBackColor2 = value; }
		} Color _groupBackColor2 = Color.FromArgb(200, 205, 218);
		#endregion

		#region Methods
		/// <summary>
		/// Draws a group item
		/// </summary>
		private void DrawGroupItem(DrawTreeNodeEventArgs e)
		{
			Rectangle nodeTextRect = e.Bounds;

			nodeTextRect.Y += 1;
			nodeTextRect.Height -= 1;

			// Draw Background
			if ((e.State & TreeNodeStates.Marked) == TreeNodeStates.Marked
				|| (e.State & TreeNodeStates.Selected) == TreeNodeStates.Selected)
			{

				nodeTextRect.Width -= 1;
				nodeTextRect.Height -= 1;

				using (Brush selBrush = new SolidBrush(this.SelectedBackColor))
					e.Graphics.FillRectangle(selBrush, nodeTextRect);

				e.Graphics.DrawRectangle(SystemPens.HotTrack, nodeTextRect);
			}
			else
			{
				using (LinearGradientBrush lgBrush = new LinearGradientBrush(nodeTextRect, this.GroupBackColor1, this.GroupBackColor2, LinearGradientMode.Vertical))
					e.Graphics.FillRectangle(lgBrush, nodeTextRect);
			}

			// Draw Image
			if (e.Node.IsExpanded)
				e.Graphics.DrawImage(HSS.Forms.Resources.expanded, new Point(nodeTextRect.Left + 3, nodeTextRect.Top + 5));
			else
				e.Graphics.DrawImage(HSS.Forms.Resources.collapsed, new Point(nodeTextRect.Left + 3, nodeTextRect.Top + 5));

			// Draw Text
			nodeTextRect.Offset(16, 1);
			nodeTextRect.Width -= 16;
			StringFormat fmt = new StringFormat();
			fmt.Trimming = StringTrimming.EllipsisCharacter;
			fmt.FormatFlags = StringFormatFlags.NoWrap;
			e.Graphics.DrawString(e.Node.Text, this._groupHeaderFont, SystemBrushes.ControlText, nodeTextRect, fmt);
		}
		/// <summary>
		/// Draws a sub item of a group
		/// </summary>
		/// <param name="e"></param>
		private void DrawItem(DrawTreeNodeEventArgs e)
		{
			Rectangle nodeTextRect = e.Bounds;

			nodeTextRect.Width -= 1;
			nodeTextRect.Height -= 1;

			ToolboxNode node = e.Node as ToolboxNode;
			if (null == node)
				return;

			if (node.IsInEdit)
			{
				// Draw Background
				e.Graphics.FillRectangle(SystemBrushes.Window, nodeTextRect);
				e.Graphics.DrawRectangle(SystemPens.HotTrack, nodeTextRect);

				// Draw Image
				if (this.ImageList != null && e.Node.ImageIndex < this.ImageList.Images.Count)
					e.Graphics.DrawImage(this.ImageList.Images[e.Node.ImageIndex], new Point(e.Bounds.Left + 3, e.Bounds.Top + 2));
			}
			else
			{
				if (node.Enabled)
				{
					// Draw Background
					if ((e.State & TreeNodeStates.Hot) == TreeNodeStates.Hot)
					{
						using (Brush hoverBrush = new SolidBrush(this.HoverBackColor))
							e.Graphics.FillRectangle(hoverBrush, nodeTextRect);
						e.Graphics.DrawRectangle(SystemPens.HotTrack, nodeTextRect);
					}
					else
					{
						if ((e.State & TreeNodeStates.Selected) == TreeNodeStates.Selected)
						{
							using (Brush hoverBrush = new SolidBrush(this.SelectedBackColor))
								e.Graphics.FillRectangle(hoverBrush, nodeTextRect);
							e.Graphics.DrawRectangle(SystemPens.HotTrack, nodeTextRect);
						}
						else
						{
							using (Brush hoverBrush = new SolidBrush(this.BackColor))
								e.Graphics.FillRectangle(hoverBrush, e.Bounds);
						}
					}

					// Draw Image
					if (this.ImageList != null && e.Node.ImageIndex < this.ImageList.Images.Count)
						e.Graphics.DrawImage(this.ImageList.Images[e.Node.ImageIndex], new Point(e.Bounds.Left + 3, e.Bounds.Top + 2));

					// Draw Text
					nodeTextRect.Offset(20, 3);
					nodeTextRect.Width -= 20;
					StringFormat fmt = new StringFormat();
					fmt.Trimming = StringTrimming.EllipsisCharacter;
					fmt.FormatFlags = StringFormatFlags.NoWrap;
					e.Graphics.DrawString(e.Node.Text, this.Font, SystemBrushes.ControlText, nodeTextRect, fmt);
				}
				else
				{
					// Draw Background
					using (Brush hoverBrush = new SolidBrush(this.BackColor))
						e.Graphics.FillRectangle(hoverBrush, e.Bounds);

					// Draw Image
					if (this.ImageList != null && e.Node.ImageIndex < this.ImageList.Images.Count)
						ControlPaint.DrawImageDisabled(e.Graphics, this.ImageList.Images[e.Node.ImageIndex],
						  e.Bounds.Left + 3, e.Bounds.Top + 2, this.BackColor);

					// Draw text Disabled
					nodeTextRect.Offset(20, 3);
					ControlPaint.DrawStringDisabled(e.Graphics, e.Node.Text, this.Font, this.BackColor, nodeTextRect, new StringFormat());
				}
			}
		}
		/// <summary>
		/// Handles the end edit event.
		/// 
		/// Hides the edit text box and stores the entered
		/// value if needed.
		/// </summary>
		/// <param name="setNewValues">Bool if the new text should be stored.</param>
		private void EndLabelEdit(bool setNewValues)
		{
			if (setNewValues && this._labelEditBox.Tag != null)
				(this._labelEditBox.Tag as ToolboxNode).Text = this._labelEditBox.Text;

			this._labelEditBox.Visible = false;

			if (this._labelEditBox.Tag != null)
			{
				(this._labelEditBox.Tag as ToolboxNode).IsInEdit = false;
				this._labelEditBox.Tag = null;
			}

			Invalidate();
		}
		#endregion

		#region Event Handlers
		/// <summary>
		/// Handles the lost forcus event of the edit text box.
		/// Ends the label editing by calling the coresponding method.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void mLabelEditBox_LostFocus(object sender, EventArgs e)
		{
			EndLabelEdit(false);
		}
		/// <summary>
		/// Handles the key down event.
		/// Calls the end label edit method and decides depending on the
		/// pressed key if the new test should be stored.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void mLabelEditBox_KeyDown(object sender, KeyEventArgs e)
		{
			switch (e.KeyCode)
			{
				case Keys.Escape:
					{
						EndLabelEdit(false);
						break;
					}
				case Keys.Enter:
					{
						EndLabelEdit(true);
						break;
					}
				default:
					{
						break;
					}
			}
		}
		#endregion

		#region ToolboxNode
		/// <summary>
		/// A ToolboxNode
		/// </summary>
		public class ToolboxNode : TreeNode
		{
			#region Constructor
			/// <summary>
			/// Constructor
			/// </summary>
			public ToolboxNode()
				: base()
			{
				this._toolTipCaption = string.Empty;
				this._isInEdit = false;
				this._enabled = true;
			}
			/// <summary>
			/// Constructor
			/// </summary>
			/// <param name="text">The text for the Node</param>
			public ToolboxNode(string text)
				: base(text)
			{
				this._toolTipCaption = string.Empty;
				this._isInEdit = false;
				this._enabled = true;
			}
			/// <summary>
			/// Constructor
			/// </summary>
			/// <param name="text">The text for the Node</param>
			/// <param name="toolTip">The tool tip caption</param>
			public ToolboxNode(string text, string toolTip)
				: base(text)
			{
				this._toolTipCaption = toolTip;
				this._isInEdit = false;
				this._enabled = true;
			}			
			#endregion

			#region Properties
			/// <summary>
			/// Gets or sets the Caption of the Tooltip
			/// </summary>
			public string ToolTipCaption
			{
				get { return this._toolTipCaption; }
				set { this._toolTipCaption = value; }
			} string _toolTipCaption;
			/// <summary>
			/// Gets or sets a flag indicating this node is being edited
			/// </summary>
			public bool IsInEdit
			{
				get { return this._isInEdit; }
				set { this._isInEdit = value; }
			} bool _isInEdit;
			/// <summary>
			/// Gets or sets if the Node is enabled
			/// </summary>
			public bool Enabled
			{
				get { return this._enabled; }
				set { this._enabled = value; }
			} bool _enabled;
			#endregion
		}
		#endregion
	}
	#endregion
}